#include "FKApplication.h"

#include "FKLogger.h"
#include "FKManagerType.h"

#include "FKUserInterface.h"
#include "FKLocalObjectManager.h"
#include "FKServerObjectManager.h"
#include "FKClientObjectManager.h"
#include <QPluginLoader>
#include <QFile>
#include <QDir>
#include <QTimer>
#include "FKObject.h"
#include "FKUser.h"
#include "FKClientData.h"
#include "FKMessage.h"
#include "FKClient.h"
#include "FKRoom.h"
#include "FKActor.h"
#include "FKEvent.h"
#include "FKFilesList.h"
#include "FKFileInfo.h"
#include <iostream>
#include "FKPostOperations.h"
#include "FKFileSender.h"
#include "FKWorldReferent.h"
#include "FKRoomInterface.h"

#include "version.h"
#include "../FKVisualInterface/version.h"
#include "../FKObjects/version.h"
#include "../FKGLModule/version.h"
#include "../FKDataModels/version.h"

FKApplication::FKApplication(int &argc, char **argv):QGuiApplication(argc,argv){
    FK_CBEGIN
//    qRegisterMetaTypeStreamOperators<FKModelData>("FKModelData");
//    qRegisterMetaType<FKModelData>();
    _UI = 0;
    _manager=0;
    setOrganizationName(FKPaths::orgName);
    setApplicationName(FKPaths::appName);
//    Tt* t=new Tt();
//    installEventFilter(t);
    FK_CEND
}

FKApplication::~FKApplication(){
    FK_DBEGIN
//            delete _UI;
    FK_DEND
}

//QSize FKApplication::screenSize(){
//    static QSize s;
//    if(s!=QSize())return s;
//    s=QApplication::desktop()->size();
//    return s;
//}

int FKApplication::mew(){
//    FKSystemObject::addClass<FKObject>();
    FKSystemObject::addClass<FKEvent>();
    FKSystemObject::addClass<FKClient>();
    FKSystemObject::addClass<FKUser>();
    FKSystemObject::addClass<FKRoom>();
    FKSystemObject::addClass<FKClientData>();
    FKSystemObject::addClass<FKMessage>();
    FKSystemObject::addClass<FKActor>();
    loadRoomPlugins();
    refreshAppVersion(initAppVersion());
    setup();
    setDefaults();
    _UI->show();
    QTimer::singleShot(2000,this,SLOT(start()));
    return exec();
}

QList<FKFileInfo> FKApplication::initAppVersion(QList<FKFileInfo> ver){
    ver.append(FKApplicationLib::version());
    ver.append(FKObjectLib::version());
    ver.append(FKVisualInterfaceLib::version());
    ver.append(FKGLModuleLib::version());
    ver.append(FKDataModelsLib::version());

    const QString mark(QT_VERSION_STR);
    ver.append(FKFileInfo::makeLibrarySet(QString("Qt5Cored"),mark));
    ver.append(FKFileInfo::makeLibrarySet(QString("Qt5Guid"),mark));
    ver.append(FKFileInfo::makeLibrarySet(QString("Qt5Widgetsd"),mark));
    ver.append(FKFileInfo::makeLibrarySet(QString("Qt5Networkd"),mark));
    ver.append(FKFileInfo::makeLibrarySet(QString("Qt5Multimediad"),mark));
    ver.append(FKFileInfo::makeLibrarySet(QString("Qt5Qmld"),mark));
    ver.append(FKFileInfo::makeLibrarySet(QString("Qt5Quickd"),mark));

    ver.append(FKFileInfo::makePlatformSet(QString("version"),QString::number(3)));

    QString cc("dependency");
#ifdef Q_CC_MINGW
//    QString cc("mingw");
    ver.append(FKFileInfo::makeLibrarySet(QString("icudt51"),cc));
    ver.append(FKFileInfo::makeLibrarySet(QString("icuin51"),cc));
    ver.append(FKFileInfo::makeLibrarySet(QString("icuuc51"),cc));
    ver.append(FKFileInfo::makeLibrarySet(QString("libgcc_s_dw2-1"),cc));
    ver.append(FKFileInfo::makeLibrarySet(QString("libstdc++-6"),cc));
    ver.append(FKFileInfo::makeLibrarySet(QString("libwinpthread-1"),cc));
    FKFileInfo plt=FKFileInfo::makeLibrarySet(QString("qwindowsd"),cc);
    plt.relativePath+=QString("platforms/");
    ver.append(plt);
#endif
#ifdef Q_CC_MSVC
//    QString cc("msvc");
    ver.append(FKFileInfo::makeLibrarySet(QString("msvcp100d"),cc));
    ver.append(FKFileInfo::makeLibrarySet(QString("msvcr100d"),cc));
    ver.append(FKFileInfo::makeLibrarySet(QString("icudt51"),cc));
    ver.append(FKFileInfo::makeLibrarySet(QString("icuin51"),cc));
    ver.append(FKFileInfo::makeLibrarySet(QString("icuuc51"),cc));
    ver.append(FKFileInfo::makeLibrarySet(QString("libGLESv2d"),cc));
    ver.append(FKFileInfo::makeLibrarySet(QString("libEGLd"),cc));
    FKFileInfo plt=FKFileInfo::makeLibrarySet(QString("qwindowsd"),cc);
    plt.relativePath+=QString("platforms/");
    ver.append(plt);
#endif
    ver.append(FKFileInfo::makeDirectorySet(applicationDirPath(),QString("QtQuick/"),QString("QtQuick"),mark));
    ver.append(FKFileInfo::makeDirectorySet(applicationDirPath(),QString("QtQuick.2/"),QString("QtQuick2"),mark));

    foreach(FKRoomModule* r,_roomModules)ver.append(r->version());

    return ver;
}

void FKApplication::refreshAppVersion(QList<FKFileInfo> ver){
    QDir root(applicationDirPath());
    FKFilesList fl(ver);
    fl.saveToFile(QFileInfo(root,FKPaths::infoListFile).absoluteFilePath());
    _appVersion=FKFilesList::loadFromPlatformFolders(root);
    _appVersion.refreshWeights(root);
}

FKFilesList FKApplication::appVersion(){
    return _appVersion;
}

//bool FKApplication::setCSSFromFile(QString &fName){
//    QFile cssf(fName);
//    if(!cssf.open(QFile::ReadOnly)) return false;
//    QString csss = QLatin1String(cssf.readAll());
//    setStyleSheet(csss);
//    return true;
//}

void FKApplication::start(){
    _UI->setAvaliableRoomTypes(_roomModules.values());
    _UI->start();
}

void FKApplication::setup(){}

void FKApplication::setUI(FKUserInterface* ui){
    if(!_UI){
        if(!ui){
            ui=new FKUserInterface(this);
            ui->setMenuInterface();
        }
        _UI=ui;
        connect(_UI,SIGNAL(managerRequested(QString,QString,int)),SLOT(managerRequested(QString,QString,int)));

        connect(_UI,SIGNAL(appFilesReplaceRequested(QStringList,QList<quint8>)),SLOT(appFilesReplaceRequested(QStringList,QList<quint8>)));
        connect(_UI,SIGNAL(appFileUpdateRequested(QString,quint8)),SLOT(requestAppFileUpdate(QString,quint8)));
        connect(_UI,SIGNAL(stopAppFilesDLRequested()),SLOT(stopAppFilesDLRequested()));
    }
}

QMap<QString,QString> FKApplication::langs(){
    return _langs;
}

void FKApplication::addLang(QString fullName, QString shortName){
    if(fullName==QString() && shortName==QString()){
        if(_langs.isEmpty())_langs[QString("RUS")]=QString("Русский");
        return;
    }
    _langs[shortName]=fullName;
}

FKUserInterface* FKApplication::getUI(){
    return _UI;
}

void FKApplication::setDefaults(){
    addLang();
    setUI();
//    _UI->regWorld(&FKWorldReferent::staticMetaObject);
//    _UI->regInterface(&FKRoomInterface::staticMetaObject);
}

void FKApplication::managerRequested(QString managerType, QString ip, int port){
    if(_manager){
        _UI->managerReseted();
        _manager->reset();
        _manager->deleteLater();
        _manager=0;
        FKObject::setManager(0);
    }
    if(managerType==FKManagerType::single){
        _manager=new FKLocalObjectManager();
        FKObject::setManager(_manager);
        FKUser::setNameReestr(managerType);
        connect(_manager,SIGNAL(clientSetted(FKClient*)),_UI,SLOT(setClient(FKClient*)));
        connect(_manager,SIGNAL(message(QString)),_UI,SLOT(message(QString)));
        connect(_UI,SIGNAL(newAction(FKEvent*)),_manager,SLOT(action(FKEvent*)));
        connect(_UI,SIGNAL(clientRequested(QString,QString)),_manager,SLOT(clientRequested(QString,QString)));
        connect(_UI,SIGNAL(genClient(QString,QString)),_manager,SLOT(genClient(QString,QString)));
        _UI->managerCreated(managerType);
    }else if(managerType==FKManagerType::host){
        FKServerObjectManager* server=new FKServerObjectManager();
        if(server->startServer(port)){
            _manager=server;
            _manager->setServerFiles(QVariant::fromValue(appVersion()));
            connect(_manager,SIGNAL(clientSetted(FKClient*)),_UI,SLOT(setClient(FKClient*)));
            connect(_manager,SIGNAL(message(QString)),_UI,SLOT(message(QString)));
            connect(_UI,SIGNAL(newAction(FKEvent*)),_manager,SLOT(action(FKEvent*)));
            connect(_UI,SIGNAL(clientRequested(QString,QString)),_manager,SLOT(clientRequested(QString,QString)));
            connect(_UI,SIGNAL(genClient(QString,QString)),_manager,SLOT(genClient(QString,QString)));
            _UI->managerCreated(managerType);
            FKObject::setManager(_manager);
            FKUser::setNameReestr(managerType);
            FKFileManager::setRootPath(applicationDirPath());
            FKFileSender::setFileMap(appVersion().fileMap(/*applicationDirPath()*/));
        }else{
            server->deleteLater();
            _UI->managerCreationFalied();
        }
    }else if(managerType==FKManagerType::client){
        FKClientObjectManager* client=new FKClientObjectManager();
        if(client->connectToServer(ip,port)){
            _manager=client;
            connect(_manager,SIGNAL(clientSetted(FKClient*)),_UI,SLOT(setClient(FKClient*)));
            connect(_manager,SIGNAL(message(QString)),_UI,SLOT(message(QString)));
            connect(client,SIGNAL(fileRecieved(QString)),SLOT(fileRecieved(QString)));
            connect(client,SIGNAL(fileError(QString)),SLOT(fileRecievingError(QString)));
            connect(client,SIGNAL(bytesRecieved(QString,qint64)),SLOT(bytesRecieved(QString,qint64)));
            connect(client,SIGNAL(disconnected()),SLOT(resetManager()));
            connect(_UI,SIGNAL(newAction(FKEvent*)),_manager,SLOT(action(FKEvent*)));
            connect(_UI,SIGNAL(clientRequested(QString,QString)),_manager,SLOT(clientRequested(QString,QString)));
            connect(_UI,SIGNAL(genClient(QString,QString)),_manager,SLOT(genClient(QString,QString)));
            _UI->managerCreated(managerType);
            FKObject::setManager(_manager);
            FKUser::setNameReestr(managerType);
            FKFileManager::setRootPath(applicationDirPath()+QString("/../downloads/bin"));
        }else{
            client->deleteLater();
            _UI->managerCreationFalied();
        }
    }else if(managerType==FKManagerType::none){

    }else{
        FK_MLOGV("Uncknown manager type requested",managerType)
    }
}

void FKApplication::resetManager(){
    managerRequested(FKManagerType::none,QString(),0);
}

void FKApplication::appFilesReplaceRequested(QStringList fl,QList<quint8> os){
    FKFilesList ff(_UI->activeClient()->serverFiles().value<FKFilesList>());
    QMap<QString,QString> m(ff.fileMapAbsolute(applicationDirPath()+QString("/../downloads/bin")));
    QString key;
    int s=fl.size();
    QStringList commands(FKPostOperations::backUpAndReplaceFiles);
    for(int i=0;i<s;++i){
        key=QString::number(os.at(i))+fl.at(i);
        if(m.contains(key)){
            commands.append(m.value(key));
            commands.append(ff.path(ff.find(fl.at(i),os.at(i))));
            addPostExecutionCommand(commands.join(';'));
            commands.clear();
            commands.append(FKPostOperations::backUpAndReplaceFiles);
        }
    }
    restartApplication();
}

void FKApplication::requestAppFileUpdate(QString fl,quint8 osType){
    FKClientObjectManager* m=qobject_cast<FKClientObjectManager*>(_manager);
    if(!m){
        FK_MLOGV("no object manager for appFileUpdateRequested",fl)
    }else{
        m->fileRequest(QString::number(osType)+fl);
    }
}

void FKApplication::stopAppFilesDLRequested(){
    FKClientObjectManager* m=qobject_cast<FKClientObjectManager*>(_manager);
    if(!m){
        FK_MLOG("no object manager for appFileUpdateInterrupt")
    }else{
        m->interruptFilesDownloading();
    }
}

void FKApplication::fileRecieved(QString fl){
    quint8 osType(fl.at(0).digitValue());
    _UI->setDownloadComplete(fl.remove(0,1),osType,true);
}

void FKApplication::fileRecievingError(QString fl){
    quint8 osType(fl.at(0).digitValue());
    _UI->setDownloadComplete(fl.remove(0,1),osType,false);
}

void FKApplication::loadRoomPlugins(){
    QDir dir(this->applicationDirPath());
    dir.cd(QString("./rooms"));
    foreach(QString file, dir.entryList(QDir::Files)){
        QPluginLoader loader(dir.absoluteFilePath(file));
        FKRoomModule* r=qobject_cast<FKRoomModule*>(loader.instance());
        if(r)_roomModules.insert(r->roomClass(),r);
    }
    FKRoom::setAvaliableRoomModules(_roomModules);
}

void FKApplication::bytesRecieved(QString fl, qint64 size){
    quint8 osType(fl.at(0).digitValue());
    _UI->setDownloaded(fl.remove(0,1),osType,size);
}

void FKApplication::addPostExecutionCommand(QString command){
    std::cout<<'@'<<QString::number(command.size()).rightJustified(4,'0').toStdString()<<command.toStdString();
}

void FKApplication::restartApplication(){
    addPostExecutionCommand(FKPostOperations::restartApplication);
    quit();
}
